Release 10.1A: OpenEdge Development:
Progress 4GL Handbook
Manipulating rows in the browse
This section describes different ways you can change browse rows.
Refreshing browse rows
If you use a read-only browse and a user updates a record in some way outside the browse, you need a way to refresh the currently focused browse row with the new data. Use the following statement form at the end of the update code to refresh the browse:
Repositioning focus
The browse and its query must remain in sync. At times, you might have to do some behind-the-scenes manipulation of the result set to keep them in sync. Generally speaking, whenever the user repositions the browse by selecting a new row, its query repositions automatically to that same row. Likewise, if the query is programmatically repositioned to a different row, the browse automatically coordinates with it and the new current row within the query becomes the currently selected row in the browse. However, if you simply
FINDa row using the query’s buffer, this does not reposition either the query or its browse. This is why you might need to resync the query in your code in order to properly refresh the browse. To do this, you can use theREPOSITIONstatement. TheREPOSITIONstatement moves the database cursor to the specified position and adjusts the browse viewport to display the new row.To avoid display flashing when doing programmatic repositions, you can set the
REFRESHABLEbrowse attribute toFALSE, do theREPOSITION, and then setREFRESHABLEtoTRUE. This suspends any redisplay of the browse until after the operation is complete.In addition, the
SET-REPOSITIONED-ROW( )method gives you control over the position in the viewport where the browse displays the repositioned row. The method takes two arguments:
- Its first Integer argument tells Progress which row (that is within the browse viewport) to position to. For example, if your browse displays seven rows at a time, you could use the
SET-REPOSITIONED-ROWmethod to show a newly positioned row in the middle of the viewport by using an argument value of 4.- The second Character argument to the method can be
ALWAYSorCONDITIONAL. If you specifyALWAYS, the browse is always adjusted to show the repositioned row in the specified position. If you specifyCONDITIONAL, then the browse adjusts only if the repositioned row is not already in the viewport.Note that normally you set
SET-REPOSITIONED-ROW( )once for the session for a browse to establish its behavior wherever it is used. You can also use theGET-REPOSITIONED-ROW()method to return as an Integer the current target viewport row for repositions.
![]()
To reposition the query and the browse along with it:
When you enter a value into the field and press TAB, the query and the browse are both repositioned to that row. If you enter a row that Progress can’t find or that isn’t an Order for the current Customer, Progress suppresses these errors and nothing changes in the browse. The
SET-REPOSITIONED-ROWmethod makes the new row the third row in the viewport unless it’s already displayed.For a multiple-select browse, the concepts of focus and selection separate. Selection indicates that the user has selected a record. The user can select many records. The last selected record is the one in the record buffer. Focus indicates that the record has input focus. There can be only one focused row, and it might or might not be a selected row. In a single-select browse, selection and focus are one and the same.
Updating browse rows
By default, Progress handles the process of updating data in the records managed by an updateable browse’s query. Because you should normally create applications that do not have a client-side dependency on a direct database connection, it is not advisable for you to define browses directly against database tables or to update database tables directly through a browse. Therefore, the default update behavior is described here to make you aware of what Progress does when you are browsing a database table, not to recommend that you take advantage of it. Some of these steps might not be completely clear to you until you read Chapter 17, "Managing Transactions," but it gives you an overview of the steps.
Assuming that the browse starts with the record in
NO-LOCKstate, Progress follows these steps:
- On any user action that modifies data in an editable browse cell, Progress again gets the record with a
SHARE-LOCK, which means that no other user can change the record. If the data has changed, Progress warns the user and redisplays the row with the new data.- When the user leaves the row and has made changes to the row, Progress starts a transaction (or subtransaction) and gets the record from the database with
EXCLUSIVE-LOCK NO-WAIT, which means that no other user can lock the record in any way. If no changes were made, Progress does not start a transaction.- If the
GETwithEXCLUSIVE-LOCKis successful, Progress updates the record, disconnects it (removes the lock), ends the transaction, and downgrades the lock to its original status.- If the
GETwithEXCLUSIVE-LOCKfails, Progress backs out the transaction, displays an error message, keeps the focus on the edited row, and retains the edited data.You also have the option to disable this default behavior and programmatically commit the changes by way of a trigger on the
ROW-LEAVEevent. To do this, you must supply theNO-ASSIGNoption in theDEFINE BROWSEstatement.If you define your browses against temp-tables, as you are doing in the OlineBrowse of the test procedure, then there are no record locks and no possibility of contention with other users for the records in the temp-table. Therefore, you can let Progress update the temp-table by defining the browse without the
NO-ASSIGNkeyword and then take care of updating the database from the temp-table yourself. You learn how to do this in Chapter 20, " Creating and Using Dynamic Temp-tables and Browses."Creating browse rows
In an updateable browser, you might want to allow the user to add new records to the underlying temp-table or database table. Programmatically, this requires three separate steps:
- Create a blank line in the browse viewport with the
INSERT-ROW()method and populate it with new data.INSERT-ROWtakes a single optional argument, which is the string“BEFORE”or“AFTER”. This argument tells Progress whether to insert the new row before or after the currently selected row in the browse. The default is“BEFORE”. You can use theINSERT-ROW()browse method in an empty browse. It places a new row at the top of the viewport.- Use the
CREATEstatement andASSIGNstatement to update the database or the underlying temp-table.- Add a reference to the result list with the
CREATE-RESULT-LIST-ENTRY( )method. This is the list of record identifiers that Progress uses to keep track of the set of rows in the query. This step is only necessary if you do not plan to reopen the query after the update, because reopening the query completely refreshes the list. However, this method makes reopening the query unnecessary for most applications.All three steps are required to create the record and keep the database, query, and browse in sync. Also, there are several possible side effects to allowing the user to add a record through an updateable browse. They include placing new records out of order and adding records that do not match the query. To eliminate these side effects, you can reopen the query after each new record is added.
![]()
To add a button to the test window that lets you add new OrderLines to the temp-table through the OlineBrowse:
- In the Definitions section of the
h-CustOrderWin5.wprocedure, beneath the browse define for OlineBrowse, define a handle variable calledhBrowse. You will use this variable to hold the handle of the OrderLine browse because it is needed in several places.- Define a new buffer for the OrderLine temp-table called
ttOline2. You’ll use this buffer to hold onto the values in the current temp-table record as you create a new record, and to copy some of the values from the old record to the new one.- Assign the OlineBrowse handle to the new
hBrowsevariable:
- Drop a new button onto the window where it will not be overwritten by the OrderLine browse when it is displayed.
- Name the new button btnNew and give it a label of New OrderLine.
- Define a
CHOOSEtrigger for the button:
This trigger code uses the handle of the OlineBrowse that you just assigned up in the Definitions section and inserts a new row in the browse after the currently selected row. First, it checks to make sure that there is a selected row using the browse’s
NUM-SELECTED-ROWSattribute. If the value of this attribute is zero, then the user has not selected any row. In this case, the trigger positions to the end of the browse by applying theENDevent to it and then selects that row using theSELECT-FOCUSED-ROWmethod.- Back in the procedure’s Definitions section, add this
ROW-LEAVEtrigger block for the OlineBrowse following the statement that assigns its handle tohBrowse:
This code first checks the
NEW-ROWattribute of the browse to see if the row the user left is one newly created by the New OrderLine button.Because you’re not prepared to do a full-fledged database update from the browse yet, the example simply creates a new row in the temp-table. To simplify the example, you start by copying the fields from the previous record into the new one. To do this, you
FINDtheLASTrecord in the temp-table using the second bufferttOline2. This gives you the highest LineNum value so that you can increment it for the new record. Note that this means that other initial field values are also assigned from the last record regardless of where in the viewport the user inserted the new record.You create a new temp-table record and
BUFFER-COPYthe last record into the new one, incrementing the LineNum field to give it a distinct value. This is important because the temp-table has the same unique index on the OrderNum and LineNum fields as the underlying OrderLine table in the database, so changing the LineNum has to happen in theBUFFER-COPYstatement to avoid a unique index violation.Next, the code assigns values to those columns that are enabled for input. The
INPUTkeyword on theASSIGNstatement tells Progress to take the values from the screen buffer for each column. Then the new values are displayed in the browse.Finally, you create a result list entry for the new record. If you didn’t create a result list entry the row would be lost if you were to scroll it out of the browse’s viewport. Reopening the query would also rebuild the result list, and also assure that all rows are in the proper order, so you can use either technique for getting the browse and the query back in sync.
- Run the window. Now you can add a record to the OrderLine browse:
![]()
Once again, you’ll learn how to get new records into the database in Chapter 16, " Updating Your Database and Writing Triggers."
Deleting browse rows
Deleting a record by way of a browse is a two-step process. First, you need to delete the record from the underlying temp-table or database table, and then you need to remove the record from the browse itself, along with the query’s result list. If you are browsing a database table directly and the user indicates a deletion, you should again get the records by
EXCLUSIVE-LOCKNO-WAITand then use theDELETEstatement to remove the records from the database. In the case of a temp-table, you can simply use theDELETEstatement to remove the records.Next, you use the
DELETE-SELECTED-ROWS( )method to delete one or more selected records from both the browse widget and the associated query result list.
![]()
To add a Delete button to the test window and use it to remove rows from the OlineBrowse and its temp-table:
Because the OlineBrowse is a multiple-selection browse, you can use it to delete one or more rows at once. This code walks through the set of selected rows and retrieves each one in turn using the
FETCH-SELECTED-ROWmethod. This method repositions the temp-table query to that row, so that it can be deleted. The code then uses theDELETE-SELECTED-ROWSmethod to delete all the rows from the browse itself, along with the query’s result list entries for them.If you run the test window, you can now delete one or more rows from the list of OrderLines for an Order. Remember that these records are deleted only from the temp-table. You would have to make a call to a procedure connected to the database to pass the list of rows to delete from the database table.
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |